package com.lmx.gateway.filter;

import cn.hutool.json.JSONUtil;
import com.apiclient.until.SecretUntil;
import com.lmx.commen.model.entity.InterfanceInfo;
import com.lmx.commen.model.entity.User;
import com.lmx.commen.service.InnerInterfanceInfoService;
import com.lmx.commen.service.InnerUserInterfanceInfoService;
import com.lmx.commen.service.InnerUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.reactivestreams.Publisher;
//import org.springframework.cloud.gateway.filter.GatewayFilterChain;
//import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.CoreSubscriber;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;

@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    @DubboReference(timeout = 30000)
    private InnerUserService innerUserService;
    @DubboReference
    private InnerInterfanceInfoService innerInterfanceInfoService;
    @DubboReference
    private InnerUserInterfanceInfoService innerUserInterfanceInfoService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        1. 在网关中统一处理，当用户放回成功调用后，调用次数加+1
//        2. 路由转发
//        3. 打印出请求日志
        ServerHttpRequest request = exchange.getRequest();
        log.info("请求唯一标识" + request.getId());
        log.info("请求路径" + request.getPath());
        log.info("请求地址" + request.getLocalAddress());
        log.info("请求方法" + request.getMethod());
//        4. 从请求头中获取鉴权所需数据，查询出登录用户
        HttpHeaders headers = request.getHeaders();
//        String accesskey = "lmx";
//        toDO 更具accesskey从数据库中取出sesretkey


        String accessKey = headers.getFirst("accessKey");
        String noce = headers.getFirst("noce");
        String timestamp = headers.getFirst("timestamp");
        String body = headers.getFirst("body");
        log.info("请求body:{}",body);
        String sign = headers.getFirst("sign");
        String host = headers.getFirst("interfancehost");

        String path = request.getPath().value();
        String method = request.getMethod().name();

        InterfanceInfo info = innerInterfanceInfoService.getInterfanceInfoByUrlOrMethod(path, method);
        if (info==null){
            log.info("接口不存在{},{}",path,method);
            return handleNoAuth(exchange);

        }

        User userByAccessKey = innerUserService.getUserByAccessKey(accessKey);
        if (userByAccessKey==null ){
            log.info("该用户不存在");
            return handleNoAuth(exchange);
        }
        String secretKey = userByAccessKey.getSecretKey();
//        5. 进行鉴权
        if (noce != null) {
//            ToDO 从缓存中取出使用过noce，是否包含当前的noce，如果包含，抛异常 防止重复调用
        }

//        3.判断timestamp是否在5分钟之内
        if (timestamp != null) {
            long currentTimeMillis = System.currentTimeMillis() / 100000;
            long timestampMIllis = Long.parseLong(timestamp);
            long time = 300000;
//             发送请求时间距离限制啊时间超过五分钟
            if (currentTimeMillis - timestampMIllis > time) {
                return handleNoAuth(exchange);
            }

        }
//        4.获取scrept进行加密计算
        String serverSign = SecretUntil.getSign(body, secretKey);


//         如果鉴权不通过结束调用
        if (sign == null || !sign.equals(serverSign)) {
            log.info("secretKey不匹配");
            return handleNoAuth(exchange);
        }

//      TODo  6. 接口是否存在，接口状态是否可调用（在后端接口中已存在）



        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        // probably should reuse buffers
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        //释放掉内存
                        DataBufferUtils.release(dataBuffer);
                        String s = new String(content, Charset.forName("UTF-8"));
//                        TODO，s就是response的值，想修改、查看就随意而为了
                        log.info("响应状态" + originalResponse.getStatusCode());
                        log.info("响应参数" + s);
                        if (originalResponse.getStatusCode() == HttpStatus.OK) {
//
                            innerUserInterfanceInfoService.invokeCount(info.getId(), userByAccessKey.getId());
                            log.info("调用次数+1");
//                            调用次数+1
                        }
                        byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                // if body is not a flux. never got there.
                return super.writeWith(body);
            }
        };
        // replace response with decorator
        return chain.filter(exchange.mutate().response(decoratedResponse).build());

    }

    private Mono<Void> handleNoAuth(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
